#include <iostream>
#include <unistd.h>
#include <string>
#include <cstdlib>
#include <vector>
#include<ctime>
#include<sys/types.h>
#include<sys/wait.h>
#include<sys/stat.h>
#include"task.hpp"
using namespace std;
enum
{
    UsageError = 1,
    ArgError,
    PipeError
};

class Channel
{
private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;

public:
    Channel(int wfd, pid_t sub_id, string &name)
        : _wfd(wfd),
          _sub_process_id(sub_id),
          _name(name)
    {
    }

    void DeBug()
    {
        cout << "_wfd:" << _wfd << endl;
        cout << "_sub_process_id:" << _sub_process_id << endl;
        cout << "_name:" << _name << endl;
    }
    string name(){return _name;}
    int wfd(){return _wfd;}
    pid_t sub_process_id(){return _sub_process_id;}
    void Close(){close(_wfd);}
    ~Channel() {}
};



class ProcessPool
{
private:
    int _sub_process_num;
    vector<Channel> _channels;

public:
    int CreateProcess(work_t work)
    {
        for (int i = 0; i < _sub_process_num; i++)
        {
            int pipefd[2]{0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                cout << "pipe error!" << endl;
                return PipeError;
            }

            pid_t id = fork();
            if (id == 0)
            {
                // child read,close 1
                close(pipefd[1]);
                // TODO something
                dup2(pipefd[0],0);
                
                work(pipefd[0]);    // subprocess execute the work method.
                exit(0);
            }

            string cname = "Channel-" + to_string(i);

            // father write,close 0
            close(pipefd[0]);
            _channels.push_back(Channel(pipefd[1], id, cname));
        }
        return 0;
    }
    // 确保是均衡选择的channel
    int NextChannel()
    {
        static int next = 0;
        int c =  next++;
        next%=_channels.size();
        return c;
    }
    // let all the subprocess exit,only need close the main master's writer.
    void KillAll()
    {
        for(auto channel:_channels){
            channel.Close();
            cout<<channel.name()<<" closed "<<" sub_process quit now "<<channel.sub_process_id()<<endl;
        }
    }
    void Wait()
    {
        for(auto channel:_channels){
            pid_t pid = channel.sub_process_id();
            pid_t rid = waitpid(pid,nullptr,0);
            if(pid == rid){
                cout<<"wait sub_process quit now "<<pid<<endl;
            }

        }
    }
    // 将任务发送到指定进程.
    void SendTaskCode(int index,uint32_t code)
    {
        cout<<"sendCode is:"<<code<<" to "<<_channels[index].name()<<"sub_process_id is :"<<_channels[index].sub_process_id()<<endl;
        write(_channels[index].wfd(),&code,sizeof(code));
    }
    void Debug()
    {
        for (auto channel : _channels)
        {
            channel.DeBug();
        }
    }
    ProcessPool(int sub_process_num)
        : _sub_process_num(sub_process_num)
    {
    }
    ~ProcessPool() {}
};

void CtrlProcessPool(ProcessPool* processpool_ptr,int cnt)
{
    while(cnt){
        // a. 选择一个进程和通道执行
        int channel = processpool_ptr->NextChannel();
        // b.要选择一个任务
        uint32_t command_code = NextTask();
        // c.发送任务.
        processpool_ptr->SendTaskCode(channel,command_code);
        sleep(1);
        cnt--;
    }
}

// ./processpool 5
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        cout << "Usage:./processpool <num>" << endl;
        return UsageError;
    }
    int sub_process_num = stoi(argv[1]);
    if (sub_process_num <= 0)
    {
        cout << "Usage:./processpool <num>,The num must > 0!" << endl;
        return ArgError;
    }
    srand((uint64_t)time(nullptr));
    // 1.create pipe  and sub_process this have a bug!!
    ProcessPool* processpool_ptr = new ProcessPool(sub_process_num);
    
    processpool_ptr->CreateProcess(worker);
    processpool_ptr->Debug();

    // 2.control sub-process
    CtrlProcessPool(processpool_ptr,5);
    cout<<"task Run Done!"<<endl;
    // 3.wait sub process.
    processpool_ptr->KillAll();

    //4.need to wait the subprocess!
    processpool_ptr->Wait();

    delete processpool_ptr;
    return 0;
}